#pragma once

#include"generators.h"
#include"LCS.h"
#include"list_of_errors.h"
#include"find_editor_path.h"
#include"string_conversation.h"
#include"check.h"
#include<Windows.h>
#include<iomanip>
#include<algorithm>
#include<stdexcept>
#include<string>
#include<iostream>
#include<fstream>

void unitSignedArrayTest(){
	std::vector<long long> values(201);
	SignedArray<long long> newSignedArray(201,100);
	for (int i = -100; i <= 100; ++i){
		long long currentValue = generate_number();
		values[i + 100] = currentValue;
		newSignedArray[i] = currentValue;
	}
	for (int i = -100; i <= 100; ++i){
		if (values[i + 100] != newSignedArray[i]){
			throw std::runtime_error("Signed array doesn't work correctly\n");
		}
	}
}

void unitTestPrefixes(){
	{
		std::vector<long long> sequence1{ 1 };
		std::vector<long long> sequence2;
		if (maxPrefix(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) != 0){
			
			throw std::runtime_error(errors[12]);
		}
	}

	{
		std::vector<long long> sequence1{ 1 };
		std::vector<long long> sequence2{ 1, 2 };
		if (maxPrefix(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) != 1){
			
			throw std::runtime_error(errors[12]);
		}
	}
	{
		std::vector<long long> sequence1{ 1, 3 };
		std::vector<long long> sequence2{ 1, 2 };
		if (maxPrefix(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) != 1){
			
			throw std::runtime_error(errors[12]);
		}
	}
	{
		std::vector<long long> sequence1{ 1, 2 };
		std::vector<long long> sequence2{ 1, 2 };
		if (maxPrefix(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) != 2){
			
			throw std::runtime_error(errors[12]);
		}
	}
	{
		std::vector<long long> sequence1{ 1, 2, 1999999, 23, 45, 54 };
		std::vector<long long> sequence2{ 1, 2, 1999999, 46, 43 };
		if (maxPrefix(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) != 3){
			
			throw std::runtime_error(errors[12]);
		}
	}
	
}
void checkingMaxPrefixesCorrectness(){
	for (int tests = 1; tests <= TEST_VALUES; ++tests){
		std::vector<long long> sequence1 = generate_sequence();
		std::vector<long long> sequence2 = spoil_some_numbers_with_delete(sequence1);
		int found_answer = maxPrefix(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end());
		if (found_answer > sequence1.size() || found_answer > sequence2.size()){
			
			throw std::runtime_error(errors[1]);
		}

		for (int i = 0; i < found_answer; ++i){
			if (sequence1[i] != sequence2[i]){
				
				throw std::runtime_error(errors[3]);
			}
		}
		if (found_answer < sequence1.size() && found_answer < sequence2.size() && sequence1[found_answer] == sequence2[found_answer]){
			
			throw std::runtime_error(errors[4]);
		}

	}
	
}


void unitTestsLCS(){
	{
		std::vector<long long> sequence1{ 1 };
		std::vector<long long> sequence2;
		if (get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) != 1){
			
			throw std::runtime_error(errors[14]);
		}
	}
	{
	std::vector<long long> sequence1{ 1 };
	std::vector<long long> sequence2{ 2 };
	if (get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) != 2){
		
		throw std::runtime_error(errors[14]);
	}
	}
	{
		std::vector<long long> sequence1{ 1, 2 };
		std::vector<long long> sequence2{ 2 };
		if (get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) != 1){
			
			throw std::runtime_error(errors[14]);
		}
	}
	{
		std::vector<long long> sequence1{ 1, 2, 3 };
		std::vector<long long> sequence2{ 1 };
		if (get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) != 2){

			throw std::runtime_error(errors[14]);
		}
	}
	{
		std::string sequence1 = "a";
		std::string sequence2 = "abc";
		//cerr << get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) << endl;
		if (get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) != 2){
			throw std::runtime_error(errors[14]);
		}
	}
	{
		std::vector<long long> sequence1{ 1, 2 };
		std::vector<long long> sequence2{ 2, 3, 4 };
		//cerr << get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) << endl;
		if (get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) != 3){
			throw std::runtime_error(errors[14]);
		}
	}
	{
		std::vector<long long> sequence1{ 2,3,5,1 };
		std::vector<long long> sequence2{ 2, 3, 4 };
		//cerr << get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) << endl;
		if (get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) != 3){
			throw std::runtime_error(errors[14]);
		}
	}
	{
		std::vector<long long> sequence1{ 2, 3, 5, 1 };
		std::vector<long long> sequence2{ 2, 1, 3, 4 };
		//cerr << get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) << endl;
		if (get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) != 4){
			throw std::runtime_error(errors[14]);
		}
	}
	
}

int vagnerFisherCheck(const std::vector<long long>& sequence1, const std::vector<long long>& sequence2){
	std::vector<std::vector<int> > VagnerFischer(sequence1.size() + 1, std::vector<int>(sequence2.size() + 1, 0));
	for (size_t i = 0; i <= sequence1.size(); ++i)
		VagnerFischer[i][0] = i;
	for (size_t j = 1; j <= sequence2.size(); ++j)
		VagnerFischer[0][j] = j;
	for (int i = 1; i <= sequence1.size(); ++i){
		for (int j = 1; j <= sequence2.size(); ++j){
			if (sequence1[i - 1] == sequence2[j - 1])
				VagnerFischer[i][j] = VagnerFischer[i - 1][j - 1];
			else{
				VagnerFischer[i][j] = min(VagnerFischer[i - 1][j], VagnerFischer[i][j - 1]) + 1;
			}
		}
	}
	return VagnerFischer[sequence1.size()][sequence2.size()];
}
void checkingEditorDifference(){
	
	for (int test = 1; test <= TEST_VALUES; ++test){
		std::vector<long long> sequence1 = generate_sequence_small();
		std::vector<long long> sequence2 = spoil_some_numbers(sequence1);
		int result = get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end());
		int correctAnswer = vagnerFisherCheck(sequence1, sequence2);
		if (correctAnswer != result){
			throw std::logic_error(errors[7]);
		}
	}
}

void unitCheckEditorPath(){
	{
		std::vector<long long> sequence1{ 1 };
		std::vector<long long> sequence2;
		int LCS_Difference = get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end());
		std::vector<std::pair<char, long long> > editingWay;
		Find_Editor_Path(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end(), LCS_Difference, editingWay);
		checkEditorCorrectness(sequence1, sequence2, editingWay);
	}
	{
		std::vector<long long> sequence1{ 1 };
		std::vector<long long> sequence2{ 2 };
		int LCS_Difference = get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end());
		std::vector<std::pair<char, long long> > editingWay;
		Find_Editor_Path(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end(), LCS_Difference, editingWay);
		checkEditorCorrectness(sequence1, sequence2, editingWay);
	}

	{
		std::vector<long long> sequence1{ 1, 2 };
		std::vector<long long> sequence2{ 2 };
		int LCS_Difference = get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end());
		std::vector<std::pair<char, long long> > editingWay;
		Find_Editor_Path(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end(), LCS_Difference, editingWay);
		checkEditorCorrectness(sequence1, sequence2, editingWay);
	}
	{
		std::vector<long long> sequence1{ 1, 2 };
		std::vector<long long> sequence2{ 2, 3 };
		int LCS_Difference = get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end());
		std::vector<std::pair<char, long long> > editingWay;
		Find_Editor_Path(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end(), LCS_Difference, editingWay);
		checkEditorCorrectness<long long>(sequence1, sequence2, editingWay);
	}
	{
		std::vector<long long> sequence1{ 1, 2 };
		std::vector<long long> sequence2{ 2, 3, 4 };
		//cerr << get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) << endl;
		int LCS_Difference = get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end());
		std::vector<std::pair<char, long long> > editingWay;
		Find_Editor_Path(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end(), LCS_Difference, editingWay);
		checkEditorCorrectness(sequence1, sequence2, editingWay);
	}
	{
		std::vector<long long> sequence1{ 2, 3, 5, 1 };
		std::vector<long long> sequence2{ 2, 3, 4 };
		//cerr << get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) << endl;
		int LCS_Difference = get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end());
		std::vector<std::pair<char, long long> > editingWay;
		Find_Editor_Path(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end(), LCS_Difference, editingWay);
		checkEditorCorrectness(sequence1, sequence2, editingWay);
	}
	{
		std::vector<long long> sequence1{ 2, 3, 5, 1 };
		std::vector<long long> sequence2{ 2, 1, 3, 4 };
		//cerr << get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end()) << endl;
		int LCS_Difference = get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end());
		std::vector<std::pair<char, long long> > editingWay;
		Find_Editor_Path(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end(), LCS_Difference, editingWay);
		checkEditorCorrectness(sequence1, sequence2, editingWay);
	}
}
void stressCheckEditorPath(){
	for (int tests = 1; tests <= TEST_VALUES; ++tests){
		cerr << tests << endl;
		std::vector<long long> sequence1 = generate_sequence();
		std::vector<long long> sequence2 = spoil_some_numbers_with_delete(sequence1);
		int LCS_Difference = get_edit_distance(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end());
		std::vector<std::pair<char, long long> > editingWay;
		Find_Editor_Path(sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end(), LCS_Difference, editingWay);
		checkEditorCorrectness(sequence1, sequence2, editingWay);
	} 
}

void streamCheckEditorPath(){
	for (int tests = 1; tests <= TEST_VALUES; ++tests){
		cerr << tests << endl;
		generate_text();
		std::vector<std::pair<char, std::string> > results = gettingChangesOfTwoFiles();
		ifstream input1("input1.txt");
		ifstream input2("input2.txt");
		std::vector<std::string> strings1;
		std::vector<std::string> strings2;
		std::string currentString;
		while (input1 >> currentString)
			strings1.push_back(currentString);
		while (input2 >> currentString)
			strings2.push_back(currentString);
		checkEditorCorrectness(strings1, strings2, results);
	}
}
